﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TestXNA3.Components;
using Microsoft.Xna.Framework;
using System.Runtime.Serialization;

namespace TestXNA3.Actors
{
    [Serializable]
    public class StaticMeshActor : Actor
    {
        private StaticMeshComponent _staticMeshComponent;
        [System.ComponentModel.Category("StaticMeshActor")]
        public StaticMeshComponent StaticMeshComponent
        {
            get { return _staticMeshComponent; }
            set { _staticMeshComponent = value; }
        }

        [System.ComponentModel.Category("StaticMeshActor")]
        public bool CastShadows
        {
            get { return _staticMeshComponent.CastShadow; }
            set { _staticMeshComponent.CastShadow = value; }
        }

        [System.ComponentModel.Browsable(false)]
        public String StaticMesh
        {
            get { return _staticMeshComponent.MeshName; }
            set { _staticMeshComponent.MeshName = value; }
        }

        private PhysicsComponent _physicsComponent;
        [System.ComponentModel.Browsable(false)]
        public PhysicsComponent PhysicsComponent
        {
            get { return _physicsComponent; }
        }

        public enum EPhysicsMode
        {
            None,
            DynamicConvex,
            Static,
            DynamicBox,
            DynamicSphere
        };
        private EPhysicsMode _physicsMode = EPhysicsMode.Static;
        [System.ComponentModel.Category("StaticMeshActor")]
        public EPhysicsMode PhysicsMode
        {
            get { return _physicsMode; }
            set { if (_physicsMode != value) enablePhysics(value); _physicsMode = value; }
        }

        private float _mass;
        [System.ComponentModel.Category("StaticMeshActor")]
        public float Mass
        {
            get
            {
                float mass = _mass;
                if (_physicsComponent != null && _physicsMode != EPhysicsMode.Static && _physicsComponent.Body != null) mass = _physicsComponent.Body.Mass;
                return mass;
            }
            set
            {
                _mass = value;
                enablePhysics(PhysicsMode);
            }
        }

        private void enablePhysics(EPhysicsMode mode)
        {
            if (GameStartup.InEditor)
                return;

            if (_physicsComponent != null)
            {
                DetachComponent(_physicsComponent);
                if (_physicsComponent.Body != null)
                {
                    _physicsComponent.Body.Dispose();
                }
                _physicsComponent = null;
            }

            if (mode == EPhysicsMode.Static && _staticMeshComponent.StaticMesh.TriMesh != null)
            {
                _physicsComponent = new PhysicsComponent();
                AttachComponent(_physicsComponent);

                StillDesign.PhysX.TriangleMeshShapeDescription triShapeDesc = new StillDesign.PhysX.TriangleMeshShapeDescription();
                triShapeDesc.TriangleMesh = _staticMeshComponent.StaticMesh.TriMesh;

                StillDesign.PhysX.ActorDescription actorDesc = new StillDesign.PhysX.ActorDescription();
                actorDesc.GlobalPose = Axis;
                actorDesc.Shapes.Add(triShapeDesc);

                _physicsComponent.Body = Scene.PhysicsScene.CreateActor(actorDesc);
            }

            if (mode == EPhysicsMode.DynamicConvex && _staticMeshComponent.StaticMesh.ConvexMesh != null)
            {
                _physicsComponent = new PhysicsComponent();
                AttachComponent(_physicsComponent);

                StillDesign.PhysX.ConvexShapeDescription convexShapeDesc = new StillDesign.PhysX.ConvexShapeDescription(_staticMeshComponent.StaticMesh.ConvexMesh);
                
                StillDesign.PhysX.ActorDescription actorDesc = new StillDesign.PhysX.ActorDescription();
                if (Mass > 0.0f)
                    actorDesc.BodyDescription = new StillDesign.PhysX.BodyDescription(Mass);
                actorDesc.GlobalPose = Axis;
                actorDesc.Shapes.Add(convexShapeDesc);

                _physicsComponent.Body = Scene.PhysicsScene.CreateActor(actorDesc);
            }

            if (mode == EPhysicsMode.DynamicBox)
            {
                _physicsComponent = new PhysicsComponent();
                AttachComponent(_physicsComponent);

                StillDesign.PhysX.BoxShapeDescription boxShapeDesc = new StillDesign.PhysX.BoxShapeDescription(BoundingBoxSize);

                StillDesign.PhysX.ActorDescription actorDesc = new StillDesign.PhysX.ActorDescription();
                if (Mass > 0.0f)
                    actorDesc.BodyDescription = new StillDesign.PhysX.BodyDescription(Mass);
                actorDesc.GlobalPose = Axis;
                actorDesc.Shapes.Add(boxShapeDesc);

                _physicsComponent.Body = Scene.PhysicsScene.CreateActor(actorDesc);
            }

            if (mode == EPhysicsMode.DynamicSphere)
            {
                _physicsComponent = new PhysicsComponent();
                AttachComponent(_physicsComponent);

                StillDesign.PhysX.SphereShapeDescription sphereShapeDesc = new StillDesign.PhysX.SphereShapeDescription(BoundingBoxSize.Length() / 3.0f);

                StillDesign.PhysX.ActorDescription actorDesc = new StillDesign.PhysX.ActorDescription();
                if (Mass > 0.0f)
                    actorDesc.BodyDescription = new StillDesign.PhysX.BodyDescription(Mass);
                actorDesc.GlobalPose = Axis;
                actorDesc.Shapes.Add(sphereShapeDesc);

                _physicsComponent.Body = Scene.PhysicsScene.CreateActor(actorDesc);
            }
        }

        public StaticMeshActor()
        {
            _saveable = true;
            _physicsComponent = null;
            _staticMeshComponent = new StaticMeshComponent();
            AttachComponent(_staticMeshComponent);
        }

        public override bool Trace(Microsoft.Xna.Framework.Vector3 start, Microsoft.Xna.Framework.Vector3 end, bool traceComponents, ref float T)
        {
            if (StaticMeshComponent.StaticMesh == null)
                traceComponents = false;

            return base.Trace(start, end, traceComponents, ref T);
        }

        protected override void duplicateProperties(Actor a)
        {
            base.duplicateProperties(a);
            StaticMeshActor sma = a as StaticMeshActor;
            sma.StaticMesh = StaticMesh;
            sma.PhysicsMode = PhysicsMode;
            sma.Mass = Mass;

            StaticMeshComponent.StaticMesh.Shader.DuplicateProperties(sma.StaticMeshComponent.StaticMesh.Shader);
            a.Scale = Scale;
        }

        public override void Load(System.IO.BinaryReader reader)
        {
            DetachComponent(_staticMeshComponent);

            if (_physicsComponent != null)
                DetachComponent(_physicsComponent);

            base.Load(reader);
            _staticMeshComponent = Components[0] as StaticMeshComponent;

            int physMode = reader.ReadInt32();
            PhysicsMode = (EPhysicsMode)physMode;

            Mass = reader.ReadSingle();
            Scale = Scale;
        }

        public override void Save(System.IO.BinaryWriter writer)
        {
            base.Save(writer);
            int physMode = (int)PhysicsMode;
            writer.Write(physMode);
            writer.Write(_mass);

            if ((Scale - new Vector3(1, 1, 1)).Length() > 0.1f)
            {
                GameStartup.StaticGameStartup.GameInfo.Map.AddScaledStaticMesh(_staticMeshComponent.StaticMesh, Scale, PhysicsMode);
            }
        }

        protected override void onMapFullyLoaded(TestXNA3.Generic.Map map)
        {
            base.onMapFullyLoaded(map);

            if ((Scale - new Vector3(1, 1, 1)).Length() > 0.1f)
            {
                StillDesign.PhysX.TriangleMesh triMesh = null;
                StillDesign.PhysX.ConvexMesh convexMesh = null;
                map.FindScaledStaticMesh(_staticMeshComponent.StaticMesh, Scale, PhysicsMode, out triMesh, out convexMesh);

                if (triMesh != null)
                {
                    _staticMeshComponent.StaticMesh.TriMesh = triMesh;
                }
                if (convexMesh != null)
                {
                    _staticMeshComponent.StaticMesh.ConvexMesh = convexMesh;
                }
                enablePhysics(PhysicsMode);
            }
        }

        public override string ToString()
        {
            return "StaticMeshActor[" + StaticMeshComponent.MeshName + "]";
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("StaticMesh", StaticMesh);
            info.AddValue("CastShadows", CastShadows);
            info.AddValue("PhysicsMode", PhysicsMode);
            info.AddValue("Mass", Mass);
        }
    }
}
